﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Masuit.Tools.Dynamics.Implementation;

internal static class Arguments
{
    public static INamedEnumerable<T> FromT<T>(IEnumerable<T> arguments, IEnumerable<string> names)
    {
        return new NamedEnumerable<T>(arguments, names);
    }

    public static INamedEnumerable<object> From(IEnumerable<object> arguments, IEnumerable<string> names)
    {
        return new NamedEnumerable<object>(arguments, names);
    }

    private class NamedEnumerable<T> : INamedEnumerable<T>
    {
        private readonly IEnumerable<T> _arguments;
        private readonly IEnumerable<string> _names;

        public NamedEnumerable(IEnumerable<T> arguments, IEnumerable<string> names)
        {
            if (arguments == null)
            {
                throw new ArgumentNullException(nameof(arguments));
            }

            if (names == null)
            {
                throw new ArgumentNullException(nameof(names));
            }

            var list = arguments.ToList();
            var nameList = names.ToList();
            if (list.Count < nameList.Count)
            {
                throw new ArgumentException("arguments.Count() < names.Count()");
            }

            _arguments = list;
            _names = nameList;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _arguments.GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return _arguments.GetEnumerator();
        }

        IEnumerable<T> INamedEnumerable<T>.Positional => _arguments.Take(_arguments.Count() - _names.Count());

        IDictionary<string, T> INamedEnumerable<T>.Named => new Named(_arguments, _names);

        private class Named : IDictionary<string, T>
        {
            private readonly IEnumerable<T> _arguments;
            private readonly IEnumerable<string> _names;

            private ICollection<T> _argumentsCollection;
            private ICollection<string> _namesCollection;

            public Named(IEnumerable<T> arguments, IEnumerable<string> names)
            {
                var args = arguments as IList<T> ?? arguments.ToList();
                var ns = names as IList<string> ?? names.ToList();
                _arguments = args.Skip(args.Count - ns.Count());
                _names = ns;
            }

            private IEnumerable<KeyValuePair<string, T>> MakeEnumerable()
            {
                return _arguments.Zip(_names, (arg, name) => new KeyValuePair<string, T>(name, arg));
            }

            IEnumerator<KeyValuePair<string, T>> IEnumerable<KeyValuePair<string, T>>.GetEnumerator()
            {
                return MakeEnumerable().GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return MakeEnumerable().GetEnumerator();
            }

            void ICollection<KeyValuePair<string, T>>.Add(KeyValuePair<string, T> item)
            {
                throw new NotImplementedException();
            }

            void ICollection<KeyValuePair<string, T>>.Clear()
            {
                throw new NotImplementedException();
            }

            bool ICollection<KeyValuePair<string, T>>.Contains(KeyValuePair<string, T> item)
            {
                return MakeEnumerable().Contains(item);
            }

            void ICollection<KeyValuePair<string, T>>.CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
            {
                throw new NotImplementedException();
            }

            bool ICollection<KeyValuePair<string, T>>.Remove(KeyValuePair<string, T> item)
            {
                throw new NotImplementedException();
            }

            int ICollection<KeyValuePair<string, T>>.Count => _names.Count();

            bool ICollection<KeyValuePair<string, T>>.IsReadOnly => true;

            bool IDictionary<string, T>.ContainsKey(string key)
            {
                return _names.Contains(key);
            }

            void IDictionary<string, T>.Add(string key, T value)
            {
                throw new NotImplementedException();
            }

            bool IDictionary<string, T>.Remove(string key)
            {
                throw new NotImplementedException();
            }

            bool IDictionary<string, T>.TryGetValue(string key, out T value)
            {
                var pair = MakeEnumerable().FirstOrDefault(kv => kv.Key == key);
                value = pair.Value;
                return pair.Key != null;
            }

            T IDictionary<string, T>.this[string key]
            {
                get => MakeEnumerable().Where(kv => kv.Key == key).Select(kv => kv.Value).FirstOrDefault();
                set => throw new NotImplementedException();
            }

            ICollection<string> IDictionary<string, T>.Keys => _namesCollection ??= _names as ICollection<string> ?? _names.ToArray();

            ICollection<T> IDictionary<string, T>.Values => _argumentsCollection ??= _arguments as ICollection<T> ?? _arguments.ToArray();
        }
    }

    public static INamedEnumerable<object> Empty()
    {
        return From(Enumerable.Empty<object>(), Enumerable.Empty<string>());
    }
}
